-- VerifyMain
-- 验证线程入口

-- 代码的搜索目录
cc.FileUtils:getInstance():addSearchPath("assets/src");
cc.FileUtils:getInstance():addSearchPath("assets/res");

cc.FileUtils:getInstance():addSearchPath("src");
cc.FileUtils:getInstance():addSearchPath("res");

-- 验证客户端标识
IS_VERIFY_CLIENT = 1

-- DEBUG_MODE = 1

-- ENABLE_PRINT = 1;

require "cocos.cocos2d.Cocos2dConstants"
require "cocos.cocos2d.extern"

require("core/base/Compiler");
require("core/base/Base");
require("core/base/String");
require("core/base/File");
require("core/base/Log");
require("core/base/Debug")
require("core/3rd/luabit/bit");
require("core/lpc/LPCValue");
require("core/base/Buffer");
require("core/base/Table");
require("core/base/UserDefaultM");
require("core/base/DaemonData");
require("core/module/EventMgr");
require("core/module/MSG");
require("core/module/PktAnalyser");
require("core/module/Communicate");
require("core/module/Profiler");

-- loc
require("game/loc/localization");

require "game/base/VerifyBase"
require "game/sdk/SdkAgent"
require "game/base/options"
require "game/base/commonUtil"

-- require("game/logic/startVerifyLogic");
require("game/logic/startLogic");

require("game/base/Sequence");

-- require "Config"

function __G__TRACKBACK__(msg)
    LAST_TRACKBACK = "";
    LAST_TRACKBACK = LAST_TRACKBACK .. "------------" .. os.date() .. "----------\n";
    LAST_TRACKBACK = LAST_TRACKBACK .. "LUA ERROR: " .. tostring(msg) .. "\n";
    LAST_TRACKBACK = LAST_TRACKBACK .. debug.traceback();
    LAST_TRACKBACK = LAST_TRACKBACK.. "\n----------------------------------------\n";

    recordTraceback(LAST_TRACKBACK)

    if not isVerifyClient() then
        cc.UserDefault:getInstance():setStringForKey("track_back", LAST_TRACKBACK);
    else
        release_print(LAST_TRACKBACK);
    end

    return msg
end

local status, msg = xpcall(startLogic, __G__TRACKBACK__)
if not status then
	print(msg);
end

local gsLoginIp = "";
local gsLoginPort = "";
local logFileName = "";
local verifyCookie = "";
local telnet_port = 0;

local lastHeartbeat = 0;
local clientPid = nil;

-- 定时写 telnet 密码
local function verifyTelnetHandler()

    local password = math.random(999999);
    cclog("verifyTelnetHandler 更新 telnet 密码 : %o", password);

    local str = "更新 telnet 密码 : " .. password .. "\n";
    write_to_file(str);

    -- 更新 telnet 密码
    update_telnet_password(password);
end

-- 开启垃圾回收功能
function enableGC()
    collectgarbage("collect");
    collectgarbage("setpause", 100);
    collectgarbage("setstepmul", 10000);
end

function write_to_file(str)
    if logFileName == "" then
        return;
    end

    if not cc.FileUtils:getInstance():isDirectoryExist(getFileWritableRoot()) then
        cc.FileUtils:getInstance():createDirectory(getFileWritableRoot());
    end

    str = string.format("%s %s", os.date(), str);
    file.updateFile(str, logFileName);
end

-- 解析命令行参数
function parseCmdLine(cmdLine)
	print("cmdLine:"..cmdLine)
	-- 格式："-logFile combatclient_%s.log -gs_login_ip %s -gs_login_port %d -cc_cookie %s"
	local argList = string.explode(cmdLine, ' ');
	local i;
	for i = 1, #argList do
		if argList[i] == "-gs_login_ip" then
			gsLoginIp = argList[i + 1];
		elseif argList[i] == "-gs_login_port" then
			gsLoginPort = argList[i + 1];
		elseif argList[i] == "-logFile" then
			logFileName = argList[i + 1];
        elseif argList[i] == "-cc_cookie" then
            verifyCookie = argList[i + 1];
        elseif argList[i] == "-telnet_port" then
            telnet_port = tonumber(argList[i + 1]);
		end
	end
end

-- 验证线程初始化工作，由C++代码在初始化的时候直接调用                                                  ：
function verifyInit(cmdLine, pid)
	print("当前进程pid:"..pid)
	parseCmdLine(cmdLine);

    -- 设置下pid
    clientPid = pid;

	-- enableGC();

    -- 通讯相关初始化
    Communicate.init();

    -- 连接服务器
    DungeonVerifyM.connectGS(gsLoginIp, gsLoginPort, verifyCookie);

    -- 注册连接断开事件
    EventMgr.register("verifyMain", event.NETWORK_DISCONNECTED, function()
        if isVerifyClient() then
            os.exit(0);
        end
    end);

	print("验证线程启动");

    -- 开启 telnet 端口
    if telnet_port > 0 then
        if enable_telnet(telnet_port) then
            release_print(string.format("开启 telnet 监听端口 %d 成功。", telnet_port));
        else
            release_print(string.format("开启 telnet 监听端口 %d 失败。", telnet_port));
        end

        -- 启动定时器定期更新 telnet 密码
        verifyTelnetHandler();
        ScheduleM.createScheme("VerifyTelnet", verifyTelnetHandler, 10000, true);
    end

	release_print("验证客户端启动");
end

-- 循环主函数，由C++代码在验证线程中的while循环中调用
function verifyRun()
    -- 如果没有连接就不用管了
    if not Socket.isConnected() then
        return;
    end

	local function run()
		-- 过一段时间通知一下服务器
		if os.time() - lastHeartbeat > 1 then
			-- 每5秒钟心跳一次
			lastHeartbeat = os.time();
			Communicate.send("CMD_VERIFY_CLIENT_HEARTBEAT", {
				["data"] = {
					["pid"] = clientPid,
					["lua_mem"] = collectgarbage("count"),
				},
			});
		end

		-- 开始连接服务器
		Communicate.update();
	end

    local status, msg = xpcall(run, __G__TRACKBACK__)
    if not status then
        print(msg);
    end
end

function __VERIFY__TRACKBACK__(msg)
    msg = msg .. debug.traceback();
    return msg
end

-- 接收到验证消息
-- 消息为lua的table字符串
-- 格式 { "cmd": 命令名, "body": 消息体 }
function onVerifyMsg(msg)
    --print("验证线程收到消息:" .. msg);

    local status, msg = xpcall(function() handleMsg(msg); end, __G__TRACKBACK__)
    if not status then
        print(msg);
        -- error(msg)
    end
end

-- 处理消息
function handleMsg(msg)
    -- 解析lua表
    msg = "return " .. msg;
    local t = loadstring(msg)();

    local cmd = t.cmd;
    local body = t.body;

    --print("消息结构, cmd:" .. (cmd or "nil") .. ", body:" .. (tostring(body) or "nil"));

	-- 交给逻辑验证模块
    DungeonVerifyM.handleMsg(cmd, body);
end

